﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Windows.Input;
using AppToolkit.WPF.UI;
using GalaSoft.MvvmLight.Command;

namespace AppToolkit.WPF.Services.UserMessageHandling
{
    /// <summary>
    /// Service that stores a list of user messages.
    /// </summary>
    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(IMessageService))]
    [Export(typeof(IMessageVisualizer))]
    public class MessageService : IMessageService, IMessageVisualizer
    {
        private readonly ObservableCollection<Message> _messageHistory = new ObservableCollection<Message>();
        private const int _maxHistoryCount = 30;
        private TimeSpan _messageShowTime = TimeSpan.FromSeconds(5);

        /// <summary>
        /// CTOR
        /// </summary>
        public MessageService()
        {
            this.MessageHistory = System.Windows.Data.CollectionViewSource.GetDefaultView(_messageHistory);
            this.IsFocused = false;
        }

        // ======================================================================
        #region IMessageService interface

        /// <summary>
        /// Show the specified <see cref="Message"/> to the user.
        /// </summary>
        /// <param name="msg"></param>
        public void ShowMessage(Message msg)
        {
            int msgCount = _messageHistory.Count;
            if (msgCount >= _maxHistoryCount)
            {
                _messageHistory.RemoveAt(msgCount - 1); 
            }
            _messageHistory.Insert(0, msg);
            this.MessageHistory.MoveCurrentToFirst();

            showInfo();
        }
        #endregion

        // ======================================================================
        #region IMessageVisualizer interface
        /// <summary>
        /// Collection of previously shown messages.
        /// </summary>
        public ICollectionView MessageHistory { get; private set; }
        
        #region Property ShowInfo
        private bool _showInfo;

        /// <summary>
        /// True, if MessageVisualizer shall show the message to the user.
        /// </summary>
        public bool ShowInfo
        {
            get
            {
                return this._showInfo;
            }
            set
            {
                if (this._showInfo != value)
                {
                    this._showInfo = value;
                    OnPropertyChanged("ShowInfo");
                }
            }
        }
        #endregion

        #region HideInfo Command
        private ICommand _hideInfoCommand;

        /// <summary>
        /// Command to trigger hiding the the currently displayed message.
        /// </summary>
        public ICommand HideInfoCommand
        {
            get
            {
                if (_hideInfoCommand == null)
                {
                    _hideInfoCommand = new RelayCommand(HideInfo);
                }
                return _hideInfoCommand;
            }
        }
        #endregion

        #region ShowExceptionCommand Implementation
        ICommand _showExceptionCommand;

        /// <summary>
        /// Command to trigger showing the exception in Exception Visualizer.
        /// </summary>
        public ICommand ShowExceptionCommand
        {
            get
            {
                if (this._showExceptionCommand == null)
                {
                    this._showExceptionCommand = new RelayCommand(OnShowException, OnCanShowException);
                }
                return this._showExceptionCommand;
            }
        }

        private bool OnCanShowException()
        {
            var msg = this.MessageHistory.CurrentItem as Message;

            if (msg != null && msg.AttachedException != null)
                return true;

            return false;
        }

        private void OnShowException()
        {
            var msg = this.MessageHistory.CurrentItem as Message;

            if (msg == null)
                return;

            var dlg = new ErrorReportDialog();
            dlg.Exception = msg.AttachedException;
            dlg.ShowDialog();
        }
        #endregion

        #region Property IsFocused
        /// <summary>
        /// MessageVisualizer can set this property to true to indicate that the user explicitly is interested in the
        /// message (e.g. he has clicked into the window visualizing the message).
        /// </summary>
        public bool IsFocused { get; set; }

        /// <summary>
        /// Duration for which a message is presented to the user.
        /// </summary>
        public TimeSpan MessageShowTime
        {
            get { return this._messageShowTime; }
            set { this._messageShowTime = value; }
        }

        #endregion

        #endregion

        #region INotifyPropertyChanged Members
        /// <summary>
        /// Override suppress change notifications.
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        // ======================================================================
        #region private implementations
        private void showInfo()
        {
            var timer = new System.Windows.Threading.DispatcherTimer { Interval = _messageShowTime };
            timer.Tick += (s,a) =>
            {
                timer.Stop();
                // in case the user clicked into the window, do not hide it
                if (!this.IsFocused)
                {
                    this.ShowInfo = false; 
                }
            };

            this.ShowInfo = true;
            timer.Start();
        }

        private void HideInfo()
        {
            this.ShowInfo = false;
        }
        #endregion
    }
}
